#include <unordered_map>
#include <iostream>
#include <string>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include "Log.hpp"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <cerrno>
#include <unistd.h>
// 构建一个服务器的类，保存服务器信息

class UdbServer
{
public:
    UdbServer( uint16_t port, std::string ip = ""):
        _ip(ip), _port(port)
    {}

    //服务器初始化
    bool ServerInital()
    {
        // 1.创建套接字
        socket_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_ >= 0)
        {
            Logmessage(NOWAIN, "socker creat %d:%s\n", errno, strerror(errno));
        }

        // 2.让操作系统将该进程与我们的套接字进行强绑定，以便内核中我们信息的获取,
        // local 也需要向客户端发送信息，里面数据需要进行修改为网络数据
        struct sockaddr_in local;
        // 全0填充,可以用memset，bzero
        bzero(&local, sizeof local);
        local.sin_family = AF_INET; // 设置通信类型
        // 服务器的ID和端口未来是要将数据发送到网络中，数据就需要修改为大端。
        local.sin_port = htons(_port); 
        // 对IP地址补充：
        // 常见的是"192.234.222.111"——点分十进制字符串风格的IP地址，目的：用户方便观察
        // 分成四个领域，每个领域都是[0~255],也就是2^8,1字节，所以IP,4字节可以完全表示
        // 如果网络以上面字符串形式传输就是15字节，所以需要15字节 <-> 4字节（网络）
        local.sin_addr.s_addr =  _ip.empty() ? INADDR_ANY :  inet_addr(_ip.c_str()); //4字节的ID也需要修改

        if (bind(socket_, (struct sockaddr*)&local, sizeof local) < 0)
        {
            Logmessage(FATIL, "bind %d:%s\n", errno, strerror(errno));
            exit(1);
        }
        return true;
    }

    //服务器启动
    int start()
    {
        char buf[1024];
        while (1)
        {
            //1.接受信息
            struct sockaddr_in peer;
            bzero(&peer, sizeof peer);
            socklen_t len = sizeof peer;
            ssize_t s_len = recvfrom(socket_, buf, (sizeof buf) - 1, 0, (struct sockaddr*)&peer, &len);
            uint16_t send_port;
            std::string send_ip;
            if (s_len > 0)
            {
                buf[s_len] = 0; // 协议分析，这里我们先不说
                //解析发送目标
                send_port = ntohs(peer.sin_port);
                send_ip = inet_ntoa(peer.sin_addr);
                printf("发送方 ip:[%s] port:[%d]:%s\n",send_ip.c_str(), send_port, buf);
            }
            //2.分析信息
            // 玩法一：获取服务器命令
            // std::string result;
            // Remote_command(buf, result);
            //3.发出信息,我们选择发送回
            // std::cout << "  resual:" << result.size() << std::endl;
            // sendto(socket_, result.c_str(), result.size(), 0, (struct sockaddr*)&peer, len);

            // 玩法二：制作一个群聊系统，只要一个家伙发信息，其余成员收到所发的消息
            // 1.解析，IP如果不存在插入用户库中
            std::string key;
            char _port[4];
            snprintf(_port, sizeof _port, "-%d", send_port);
            key += send_ip += _port;
            if (cline_lib.find(send_ip) == cline_lib.end())
            {
                cline_lib.insert({key, peer});
            }
            std::string result = key;
            result  += " #";
            result += buf;
            // 2.向用户库中所有成员返回其发送的内容
            for (auto& it : cline_lib)
            {
                sendto(socket_, result.c_str(), result.size(), 0, (sockaddr*)&it.second, sizeof it.second);
            }
            Logmessage(NOWAIN, "推送完成");
        }   
    }

    void Remote_command(char* buf, std::string& result)
    {
        char tmp[256];
        // 检测里面是否有rm rmkdir指令
        if (strcasestr(buf, "rm") != nullptr|| strcasestr(buf, "mkdir") != nullptr)
        {
            result += "有内鬼，下播！";
            std::cout << result << std::endl;
        }
        FILE* ft = popen(buf,"r");
        while (fgets(tmp, sizeof tmp, ft) != nullptr)
        {
            result += tmp;
        }
    }

     ~UdbServer()
    {
        if (socket_ > 0 ) close(socket_);
    }
private:
    std::string _ip; // 该服务器ip
    uint16_t _port;  // 该端口号  
    int socket_ = -1;     // 套接字值
    std::unordered_map<std::string,sockaddr_in> cline_lib;
};




